/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
/**
 * Software License, Version 1.0 Copyright 2003 The Trustees of Indiana
 * University. All rights reserved. Redistribution and use in source and binary
 * forms, with or without modification, are permitted provided that the
 * following conditions are met: 1) All redistributions of source code must
 * retain the above copyright notice, the list of authors in the original source
 * code, this list of conditions and the disclaimer listed in this license; 2)
 * All redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the disclaimer listed in this license in the
 * documentation and/or other materials provided with the distribution; 3) Any
 * documentation included with all redistributions must include the following
 * acknowledgement: "This product includes software developed by the Community
 * Grids Lab. For further information contact the Community Grids Lab at
 * http://communitygrids.iu.edu/." Alternatively, this acknowledgement may
 * appear in the software itself, and wherever such third-party acknowledgments
 * normally appear. 4) The name Indiana University or Community Grids Lab or
 * NaradaBrokering, shall not be used to endorse or promote products derived
 * from this software without prior written permission from Indiana University.
 * For written permission, please contact the Advanced Research and Technology
 * Institute ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202. 5)
 * Products derived from this software may not be called NaradaBrokering, nor
 * may Indiana University or Community Grids Lab or NaradaBrokering appear in
 * their name, without prior written permission of ARTI. Indiana University
 * provides no reassurances that the source code provided does not infringe the
 * patent or any other intellectual property rights of any other entity. Indiana
 * University disclaims any liability to any recipient for claims brought by any
 * other entity based on infringement of intellectual property rights or
 * otherwise. LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH
 * NO WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY
 * GIVES NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF
 * INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS.
 * INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS",
 * "VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.
 * LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR
 * ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION
 * GENERATED USING SOFTWARE.
 */
package cgl.narada.transport.sslHttpBase;

import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import java.util.Properties;

import javax.net.ssl.HandshakeCompletedEvent;
import javax.net.ssl.HandshakeCompletedListener;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

import org.apache.log4j.Logger;

import cgl.narada.transport.proxy.ProxyInfo;
import cgl.narada.transport.proxy.ProxyTunnel;
import cgl.narada.util.DataQueue;

/**
 * An generic implementation of transport with a sender thread and receiver
 * thread and 2 queues. Does status checks with a maximum resolution of 1
 * second.
 * 
 * @author John Yin, Hongbin Liu
 * @author Harshawardhan Gadgil
 * @version 1.0
 */

public abstract class TransportImpl implements Transport {

	final static Logger log = Logger.getLogger(TransportImpl.class.getName());

	protected long m_statusInterval = 0;
	protected DataQueue m_sendQueue = new DataQueue();
	protected DataQueue m_receiveQueue = new DataQueue();

	// Destination end point host and port.
	protected String m_host = null;
	protected int m_port = 0;
	protected String m_localhost = ""; // Name of local host.
	protected boolean m_ioerror = false; // Indicated whether disconnect
	// caused by io errors.

	protected ProxyInfo proxyInfo = new ProxyInfo();
	protected ProxyTunnel proxyTunnel;
	protected SSLSocketFactory m_sslFactory = null;

	/**
	 * -------------------------------------------------------------------
	 * Should invoke this in child class to initialize send/receive threads.
	 */
	public abstract void startThreads() throws IOException;

	/**
	 * -------------------------------------------------------------------
	 * Should invoke this in the child class to clean up resources in
	 * TransportImpl.
	 */
	public abstract void stopThreads() throws IOException;

	/**
	 * -------------------------------------------------------------------
	 */
	public abstract void setTransportDataListener(TransportDataListener l);

	/**
	 * -------------------------------------------------------------------
	 * Properties Recognized: host - The desitnation end point host port - The
	 * destination end point port. truststore - Location of the trusted
	 * authorities database keystore - Location of the public/private key
	 * database truststorePassword - Password to the truststore keystorePassword -
	 * Password to the keystore username - Proxy authenticated user name.
	 * password - Proxy authenticated password. domain - local NT domaion [ for
	 * NTLM Authenticaton ] https.proxyHost - The location of the HTTPS proxy.
	 * Will try to auto detect from System properties if this does not exist.
	 * https.proxyPort - The location of the HTTPS proxy port. Will try to auto
	 * detec from System properties if this does not exist. secure - true |
	 * false. If false, will not do any real SSL.
	 */
	public void setProperties(Properties properties) {
		proxyInfo.getProxyInfo(properties);

		Properties systemProperties = System.getProperties();
		/**
		 * if keyStore is for client side do we need this here? val =
		 * properties.getProperty("keyStore"); if (val != null && val.length() >
		 * 0) { systemProperties.put("javax.net.ssl.keyStore",val);
		 * log.debug("Setting keyStore to "+val); } val =
		 * properties.getProperty("keyStorePassword"); if (val != null &&
		 * val.length() > 0) {
		 * systemProperties.put("javax.net.ssl.keyStorePassword",val);
		 * log.debug("Setting keyStore password to "+val); }
		 */

		String val = properties.getProperty("hostname");
		if (val != null && val.length() > 0) {
			m_host = val;
			log.debug("Will connect to host: " + m_host);
		}

		val = properties.getProperty("portnum");
		try {
			m_port = Integer.parseInt(val);
			log.debug("Will connect to port: " + m_port);
		}
		catch (Exception e) {
			log.warn("can't parse portnum");
			e.printStackTrace();
		}

		val = properties.getProperty("localhost");
		if (val != null) {
			m_localhost = val;
		}
		log.debug("Local hostname is: " + m_localhost);

	}

	/**
	 * -------------------------------------------------------------------
	 */
	public void sendData(byte[] message) throws IOException {
		if (!isConnected()) {
			throw new IOException("Not connected");
		}

		m_sendQueue.addData(message);
	}

	/**
	 * ------------------------------------------------------------------- Sets
	 * the keep alive & performance metric checks. By default, there is not
	 * status checks.
	 */
	public void setStatusCheckInterval(long statusInterval) {
		m_statusInterval = statusInterval;
	}

	/**
	 * -------------------------------------------------------------------
	 */
	public long getStatusCheckInterval() {
		return m_statusInterval;
	}

	/**
	 * -------------------------------------------------------------------
	 */
	public byte[] receiveData() throws IOException {
		return m_receiveQueue.removeData();
	}

	/**
	 * -------------------------------------------------------------------
	 */
	public byte[] receiveData(long waitTime) throws IOException {
		return m_receiveQueue.removeData(waitTime);
	}

	/**
	 * -------------------------------------------------------------------
	 */
	protected boolean handleTransportSystemMessage(byte[] data) {
		// TO DO 2
		// Implement with performance metrics.
		if (data[0] == LinkMessageKeepAlive.TRANSPORT_KEEPALIVE_TYPE) {
			log.debug("KEEPALIVE received on " + this + " @: "
					+ new Date(System.currentTimeMillis()));
			return true;
		}
		return false;
	}

	/**
	 * -------------------------------------------------------------------
	 */
	protected abstract void sendDataImmediately(byte[] message)
			throws IOException;

	/**
	 * -------------------------------------------------------------------
	 */
	protected abstract byte[] receiveDataImmediately() throws IOException;

	/**
	 * -------------------------------------------------------------------
	 */
	protected abstract void handleSendException(Exception e);

	/**
	 * -------------------------------------------------------------------
	 */
	protected abstract void handleReceiveException(Exception e);

	/**
	 * -----------------------------------------------------------------------
	 * Setup SSL over an established socket connection. Will perform SSL
	 * handshake.
	 */
	protected SSLSocket setupSSL(Socket tunnel, String host, int port)
			throws IOException {
		log.debug("setupSSL starts");
		log.debug("javax.net.ssl.trustStore="
				+ System.getProperty("javax.net.ssl.trustStore"));
		log.debug("javax.net.ssl.trustStorePassword="
				+ System.getProperty("javax.net.ssl.trustStorePassword"));

		// long starttime = System.currentTimeMillis();

		/*
		 * Ok, let's overlay the tunnel socket with SSL.
		 */
		SSLSocket socket = (SSLSocket) m_sslFactory.createSocket(tunnel, host,
				port, true);

		// long diff = System.currentTimeMillis() - starttime;
		// log.debug("Took " + diff + " ms to create SSL socket instance");

		// final long handshakeTime = System.currentTimeMillis();

		/*
		 * register a callback for handshaking completion event
		 */
		socket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
			public void handshakeCompleted(HandshakeCompletedEvent event) {
				// + (System.currentTimeMillis() - handshakeTime) + " ms"
				log.debug("Handshake finished: ");
				log.debug("CipherSuite:" + event.getCipherSuite());
				log.debug("SessionId: " + event.getSession());
				log.debug("PeerHost: " + event.getSession().getPeerHost());
			}
		});
		socket.startHandshake();
		return socket;
	}
}
